package com.example.student.service.impl;

import com.example.student.entity.SysUser;
import com.example.student.entity.SysTokenBlacklist;
import com.example.student.mapper.SysTokenBlacklistMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.crypto.SecretKey;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Calendar;

/**
 * JWT令牌服务
 * 处理JWT token的生成、验证、刷新等操作
 */
@Service
public class JwtTokenService {
    private static final Logger logger = LoggerFactory.getLogger(JwtTokenService.class);
    private static final String KEY_STORE_TYPE = "JCEKS";
    private static final String KEY_ALIAS = "jwt-signing-key";
    private static final String KEY_STORE_PATH = "jwt-keystore.jceks";
    private static final char[] KEY_STORE_PASSWORD = "jwtKeyStorePassword".toCharArray();
    private static final String SECRET_KEY_FILE = "jwt-secret-key.txt";

    @Value("${jwt.expiration}")
    private Long expiration;
    
    @Value("${jwt.refresh-expiration}")
    private Long refreshExpiration;
    
    @Resource
    private SysTokenBlacklistMapper tokenBlacklistMapper;
    
    private SecretKey secretKey;
    private JwtParser jwtParser;
    
    @PostConstruct
    public void init() {
        try {
            // 尝试从文件加载密钥，如果不存在则创建新密钥
            this.secretKey = loadOrCreateSecretKey();
            this.jwtParser = Jwts.parserBuilder().setSigningKey(secretKey).build();
            logger.info("JWT signing key initialized successfully");
        } catch (Exception e) {
            logger.error("Failed to initialize JWT signing key", e);
            // 如果出现异常，创建新密钥
            this.secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS512);
            this.jwtParser = Jwts.parserBuilder().setSigningKey(secretKey).build();
            logger.info("Created new JWT signing key as fallback");
        }
    }
    
    /**
     * 加载或创建密钥
     */
    private SecretKey loadOrCreateSecretKey() throws IOException {
        Path secretKeyPath = Paths.get(SECRET_KEY_FILE);
        
        // 如果密钥文件存在，从文件加载密钥
        if (Files.exists(secretKeyPath)) {
            String encodedKey = new String(Files.readAllBytes(secretKeyPath), StandardCharsets.UTF_8);
            byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
            return Keys.hmacShaKeyFor(decodedKey);
        }
        
        // 如果密钥文件不存在，创建新密钥并保存到文件
        SecretKey newKey = Keys.secretKeyFor(SignatureAlgorithm.HS512);
        String encodedKey = Base64.getEncoder().encodeToString(newKey.getEncoded());
        Files.write(secretKeyPath, encodedKey.getBytes(StandardCharsets.UTF_8));
        
        return newKey;
    }

    /**
     * 生成token
     */
    public String generateToken(SysUser user) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", user.getId());
        claims.put("username", user.getUsername());
        return createToken(claims, user.getId().toString(), expiration);
    }
    
    /**
     * 生成刷新token
     */
    public String generateRefreshToken(SysUser user) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", user.getId());
        claims.put("type", "refresh");
        return createToken(claims, user.getId().toString(), refreshExpiration);
    }

    /**
     * 验证token
     */
    public boolean validateToken(String token) {
        try {
            if (isTokenBlacklisted(token)) {
                return false;
            }
            Claims claims = getClaimsFromToken(token);
            Date expiration = claims.getExpiration();
            return !expiration.before(new Date());
        } catch (Exception e) {
            return false;
        }
    }
    
    /**
     * 检查token是否在黑名单中
     */
    private boolean isTokenBlacklisted(String token) {
        return tokenBlacklistMapper.selectCountByToken(token) > 0;
    }

    /**
     * 从token中获取用户信息
     */
    public Claims getClaimsFromToken(String token) {
        try {
            return jwtParser.parseClaimsJws(token).getBody();
        } catch (Exception e) {
            logger.error("Failed to parse token: {}", e.getMessage());
            throw e;
        }
    }
    
    /**
     * 从请求中获取token
     */
    public String getTokenFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
    
    /**
     * 将token加入黑名单
     */
    public void addToBlacklist(String token, Long userId) {
        SysTokenBlacklist blacklistToken = new SysTokenBlacklist();
        blacklistToken.setToken(token);
        blacklistToken.setUserId(userId);
        
        // 从token中获取过期时间
        try {
            Claims claims = getClaimsFromToken(token);
            Date expiration = claims.getExpiration();
            blacklistToken.setExpireTime(expiration);
        } catch (Exception e) {
            // 如果解析失败，设置默认过期时间为当前时间加1天
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.DAY_OF_MONTH, 1);
            blacklistToken.setExpireTime(calendar.getTime());
        }
        
        tokenBlacklistMapper.insert(blacklistToken);
    }
    
    /**
     * 刷新token
     */
    public String refreshToken(String refreshToken) {
        try {
            Claims claims = getClaimsFromToken(refreshToken);
            
            // 验证是否为刷新token
            if (!"refresh".equals(claims.get("type"))) {
                throw new IllegalArgumentException("Invalid refresh token");
            }
            
            // 验证token是否过期
            Date expiration = claims.getExpiration();
            if (expiration.before(new Date())) {
                throw new IllegalArgumentException("Refresh token has expired");
            }
            
            // 从刷新token中获取用户ID
            Long userId = Long.valueOf(claims.get("userId").toString());
            
            // 创建新的访问token
            Map<String, Object> newClaims = new HashMap<>();
            newClaims.put("userId", userId);
            newClaims.put("username", claims.get("username"));
            
            // 计算剩余过期时间（毫秒）
            long remainingTime = expiration.getTime() - System.currentTimeMillis();
            if (remainingTime <= 0) {
                remainingTime = 1000 * 60 * 30; // 如果已过期，设置30分钟过期时间
            }
            
            return createToken(newClaims, userId.toString(), remainingTime);
        } catch (Exception e) {
            throw new IllegalArgumentException("Failed to refresh token", e);
        }
    }
    
    /**
     * 创建token
     */
    private String createToken(Map<String, Object> claims, String subject, Long expirationTime) {
        Date now = new Date();
        Date expirationDate = new Date(now.getTime() + expirationTime);
        
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(now)
                .setExpiration(expirationDate)
                .signWith(secretKey, SignatureAlgorithm.HS512)
                .compact();
    }
} 